
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <wchar.h>


#include "common/alloc.h"

#include "str_hash_table.h"
#include "lex_tree.h"
#include "file_export.h"

#include "common/utf8.h"
#include "common/utf8impl.h"

//
// read and save
//

// intermediate file buffer size
#define MAX_FILE_BUF_LEN    (256*256)

// sufficient space for table line according the specification with a little overhead
#define MAX_LINE_SIZE       (8*256)



struct ImportContext
{
    struct HashTable * adm_units_hash_table;

    struct Node * root;

    FILE * fsrc;

    FILE * fdest;
};


#if 0

static inline char * read_till_tab(char ** line, char ** last)
{
    char * cur = *line;
    char * begin = cur;

    if (*cur == 0)
    {
        return 0;
    }

    while (*cur++ != 0)
    {
        if (*cur == '\t')
        {
            *last = cur;
            *cur++ = 0;
            break;
        }
    }

    *line = cur;
    return begin;
}


#define DO_READ() \
    cur = read_till_tab(&line, &last);\
    if (!cur)\
    {\
        fprintf(stderr, "can't read one another line at %s\n", orig_str);\
        return;\
    }

static inline void handle_line(struct ImportContext * ctx, char * line)
{
    char * orig_str = line;
    char * cur = NULL;
    char * last = NULL;

    char * number;
    char * name;
    char * adm_unit;
    char * adm_unit_last;

    assert(orig_str);

    DO_READ(); // number
    number = cur;

    DO_READ(); // name

    DO_READ(); // latin1 name
    name = cur;

    DO_READ(); // alternate names
    DO_READ(); // latitude
    DO_READ(); // longtitude
    DO_READ(); // feature class

    DO_READ(); // feature code
    adm_unit = cur;
    adm_unit_last = last; // make it point to the last character of current string

    if (!hash_table_contains(ctx->hash_table, adm_unit, adm_unit_last - adm_unit))
    {
        return;
    }

    fprintf(stdout, "#%s: name ='%s', unit: %s\n", number, name, adm_unit);

    // add an entire lexema
    add_lexeme(name, ctx->root);

    // add all the composite parts of the lexema
    {
        char * word_begin = name;
        char * iter;

        for (iter = name; *iter != 0; ++iter)
        {
            if (*iter == ' ')
            {
                *iter = 0;
                add_lexeme(word_begin, ctx->root);
                word_begin = iter + 1;
            }
        }

        // last word
        if (*word_begin != 0 && word_begin != name)
        {
            add_lexeme(word_begin, ctx->root);
        }
    }
}

static inline void read_to_tree(struct ImportContext * ctx)
{
    const int max_recs = 20000;
    char * buf = xmalloc(MAX_FILE_BUF_LEN);

    int bytes_read = fread(buf, 1, MAX_FILE_BUF_LEN, ctx->fsrc);

    if (bytes_read > 0)
    {
        char * begin = buf;
        const char * end = begin + bytes_read;
        int rec;

        for (rec = 0; begin != end && rec < max_recs; ++rec)
        {
            char * cur = begin;
            while (*begin != '\n')
            {
                ++begin;
                if (begin != end)
                {
                    continue;
                }

                // buffer is too small?
                if (cur == buf)
                {
                    fprintf(stderr, "error: source buffer is too small\n");
                    abort();
                }

                // can read more?
                if (bytes_read == MAX_FILE_BUF_LEN)
                {
                    char * new_buf = xmalloc(MAX_FILE_BUF_LEN);
                    size_t copied = begin - cur;

                    // copy current contents to this buffer
                    memcpy(new_buf, cur, copied);
                    xfree(buf);
                    buf = new_buf;

                    // fix new values to make them point to the new locations
                    cur = buf;
                    begin = buf + copied;

                    bytes_read = fread(begin, 1, MAX_FILE_BUF_LEN - copied, ctx->fsrc);
                    end = begin + bytes_read;

                    bytes_read += copied;
                    continue;
                }

                // go back to the previous position to put
                // trailing zero at the end of the buffer then,
                // expecting whitespace at the end of file
                --begin;

                break;
            }

            // put 0 at this string and advance to the next character
            *begin++ = 0;

            handle_line(ctx, cur);
        }
    }

    xfree(buf);
}
#endif

static inline void read_entries_data(struct ImportContext * ctx)
{
    assert(ctx);
}



static void add_adm_unit_entries(struct HashTable * ht)
{
    // adm unit entries are as follows:
    hash_table_add_len(ht, "ADM1");
    hash_table_add_len(ht, "ADM2");
    hash_table_add_len(ht, "ADM3");
    hash_table_add_len(ht, "ADM4");
    hash_table_add_len(ht, "ADMD");
    hash_table_add_len(ht, "LTER");
    hash_table_add_len(ht, "PCL");
    hash_table_add_len(ht, "PCLD");
    hash_table_add_len(ht, "PCLF");
    hash_table_add_len(ht, "PCLI");
    hash_table_add_len(ht, "PCLIX");
    hash_table_add_len(ht, "PCLS");
    hash_table_add_len(ht, "PRSH");
    hash_table_add_len(ht, "TERR");
    hash_table_add_len(ht, "ZN");
    hash_table_add_len(ht, "ZNB");
}


static const char * src_name = "/data/tmp/conc/geoname/extract/allCountries.txt";
//static const char * src_name = "/data/tmp/conc/geoname/records-seq-1024.txt";
static const char * dest_name = "/tmp/entries.1";

static inline void save_entries()
{
    FILE * fdest = fopen(dest_name, "wb");
    FILE * fsrc = fopen(src_name, "rb");
    bool ok = false;

    if (fsrc && fdest)
    {
        struct ImportContext ctx;
        ctx.fsrc = fsrc;
        ctx.fdest = fdest;
        ctx.adm_units_hash_table = create_hash_table();
        ctx.root = new_node(NULL);

        add_adm_unit_entries(ctx.adm_units_hash_table);

        read_entries_data(&ctx);

        fprintf(stdout, "Saving tree to file...\n");
        save_tree_to_file(ctx.root, fdest);

        free_hash_table(ctx.adm_units_hash_table);
        free_node(ctx.root);

        ok = true;
    }
    else
    {
        fprintf(stderr, "can't open source and(or) dest file(s)!\n");
    }

    if (fsrc)
    {
        fclose(fsrc);
    }

    if (fdest)
    {
        fclose(fdest);
    }

    if (ok)
    {
        fsrc = fopen(dest_name, "rb");
        struct FileDb * db = load_file_db(fsrc);

        file_db_print_all(db);
        //fprintf(stdout, "All begins from letter:\n");
        //print_begins_from(root, "Pa");

        free_file_db(db);
        fclose(fsrc);
    }
}

void test_import2()
{
    save_entries();
}
